package cn.itcast.demo.lock;

import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scripting.support.ResourceScriptSource;

import java.util.Collections;
import java.util.UUID;

/**
 * @author 虎哥
 */
public class ReentrantRedisLock implements RedisLock {

    private String key;

    private StringRedisTemplate redisTemplate;

    private static final String ID_PREFIX = UUID.randomUUID().toString() + "_";

    public ReentrantRedisLock(String key, StringRedisTemplate redisTemplate) {
        // 在redis中的key
        this.key = key;
        this.redisTemplate = redisTemplate;
    }

    /**
     * 获取锁的脚本
     */
    private static final DefaultRedisScript<Long> LOCK_SCRIPT;
    /**
     * 释放锁的脚本
     */
    private static final DefaultRedisScript<Object> UNLOCK_SCRIPT;

    static {
        // 构建脚本对象
        LOCK_SCRIPT = new DefaultRedisScript<>();
        // 锁的返回值类型
        LOCK_SCRIPT.setResultType(Long.class);
        // 去本地文件加载脚本
        LOCK_SCRIPT.setScriptSource(new ResourceScriptSource(new ClassPathResource("lock.lua")));

        UNLOCK_SCRIPT = new DefaultRedisScript<>();
        UNLOCK_SCRIPT.setScriptSource(new ResourceScriptSource(new ClassPathResource("unlock.lua")));
    }

    private String releaseTime;

    @Override
    public boolean lock(long releaseTime) {
        // 获取锁的释放时间
        this.releaseTime = String.valueOf(releaseTime);
        while (true) {
            // 获取当前线程标示
            String threadID = ID_PREFIX + Thread.currentThread().getId();
            // 尝试获取锁，返回值是1或0,1：获取锁成功，0：获取锁失败
            Long result = redisTemplate.execute(LOCK_SCRIPT, Collections.singletonList(key), threadID, this.releaseTime);
            // 判断是否成功，返回结果
            if (result != null && result == 1) {
                return true;
            }
            // 阻塞，等待唤醒
            // CountDownLatch latch = new CountDownLatch(1);
            // 监听通知，调用latch.cutDown();
            // latch.await();
        }
    }

    @Override
    public void unlock() {
        String threadID = ID_PREFIX + Thread.currentThread().getId();
        // 执行脚本
        redisTemplate.execute(
                UNLOCK_SCRIPT,
                Collections.singletonList(key),
                threadID, this.releaseTime);
        // 发送通知，锁已经释放了
    }
}
